////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNSystemInfo/inc/WNMemoryInfo.h"
#include "WNConcurrency/inc/WNExecuteOnce.h"

#ifdef _WN_WINDOWS
    #include <windows.h>
#elif defined _WN_LINUX
    #include <sys/types.h>
    #include <sys/sysinfo.h>
#endif

static WN_UINT64 g_TotalPhysical = 0;
static WN_UINT64 g_TotalVirtual = 0;

WN_EXECUTE_ONCE_FUNCTION(WNMemoryInfo_Init) {
    #ifdef _WN_WINDOWS
        MEMORYSTATUSEX memoryInfo;

        memoryInfo.dwLength = sizeof(MEMORYSTATUSEX);

        GlobalMemoryStatusEx(&memoryInfo);

        g_TotalPhysical = memoryInfo.ullTotalPhys;
        g_TotalVirtual = memoryInfo.ullTotalPageFile;
    #elif defined _WN_LINUX
        struct sysinfo memoryInfo;

        if (sysinfo(&memoryInfo) == 0) {
            g_TotalPhysical = memoryInfo.totalram;
            g_TotalPhysical *= memoryInfo.mem_unit;

            g_TotalVirtual = memoryInfo.totalram;
            g_TotalVirtual += memoryInfo.totalswap;
            g_TotalVirtual *= memoryInfo.mem_unit;
        }
    #endif
}

static WN_EXECUTE_ONCE_T g_Once = WN_EXECUTE_ONCE_INIT;

extern "C" {
    WN_UINT64 WNMemoryInfo_TotalPhysicalMemory() {
        WNExecuteOnce(&g_Once, &WNMemoryInfo_Init);

        return(g_TotalPhysical);
    }

    WN_UINT64 WNMemoryInfo_TotalVirtualMemory() {
        WNExecuteOnce(&g_Once, &WNMemoryInfo_Init);

        return(g_TotalVirtual);
    }

    WN_UINT64 WNMemoryInfo_AvailablePhysicalMemory() {
        WN_UINT64 availablePhysical = 0;

        #ifdef _WN_WINDOWS
            MEMORYSTATUSEX memoryInfo;

            memoryInfo.dwLength = sizeof(MEMORYSTATUSEX);

            GlobalMemoryStatusEx(&memoryInfo);

            availablePhysical = memoryInfo.ullAvailPhys;
        #elif defined _WN_LINUX
            struct sysinfo memoryInfo;

            if (sysinfo(&memoryInfo) == 0) {
                availablePhysical = memoryInfo.freeram;
                availablePhysical *= memoryInfo.mem_unit;
            }
        #endif

        return(availablePhysical);
    }

    WN_UINT64 WNMemoryInfo_AvailableVirtualMemory() {
        WN_UINT64 availableVirtual = 0;

        #ifdef _WN_WINDOWS
            MEMORYSTATUSEX memoryInfo;

            memoryInfo.dwLength = sizeof(MEMORYSTATUSEX);

            GlobalMemoryStatusEx(&memoryInfo);

            availableVirtual = memoryInfo.ullAvailPageFile;
        #elif defined _WN_LINUX
            struct sysinfo memoryInfo;

            if (sysinfo(&memoryInfo) == 0) {
                availableVirtual = memoryInfo.freeram;
                availableVirtual += memoryInfo.freeswap;
                availableVirtual *= memoryInfo.mem_unit;
            }
        #endif

        return(availableVirtual);
    }
}